﻿// -----------------------------------------------------------------------
// <copyright file="NotificationListenerService.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Acknowledgement
{
	using System.Collections.Concurrent;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading.Tasks.Dataflow;
	using Microsoft.Practices.Unity;
	using Zen.BlackBerry.Common;
	using Zen.BlackBerry.Configuration;
	using Zen.BlackBerry.Pap;
	using Zen.BlackBerry.Push;

	/// <summary>
	/// Implementation of the <see cref="INotificationListenerService"/>
	/// interface.
	/// </summary>
	public class NotificationListenerService : INotificationListenerService
	{
		private IPushProperties _pushProperties;
		private ActionBlock<Notification> _notificationQueue;
		private ConcurrentDictionary<string, List<NotificationListener>> _listeners =
			new ConcurrentDictionary<string, List<NotificationListener>>();

		/// <summary>
		/// Initializes a new instance of the <see cref="NotificationListenerService"/> class.
		/// </summary>
		/// <param name="pushProperties">The push properties.</param>
		public NotificationListenerService(
			[Dependency]IPushProperties pushProperties)
		{
			_pushProperties = pushProperties;

			_notificationQueue = new ActionBlock<Notification>(
				(notification) => ExecuteNotification(notification),
				new ExecutionDataflowBlockOptions
				{
					// TODO: Set this value from IPushProperties
					MaxDegreeOfParallelism = 4
				});
		}

		/// <summary>
		/// Adds the listener to the collection of notification listeners
		/// associated with the specified push application id.
		/// </summary>
		/// <param name="pushApplicationId">The push application id.</param>
		/// <param name="listener">The listener.</param>
		public void AddListener(string pushApplicationId, NotificationListener listener)
		{
			AddListeners(pushApplicationId, new NotificationListener[] { listener });
		}

		/// <summary>
		/// Adds the listeners to the collection of notification listeners
		/// associated with the specified push application id.
		/// </summary>
		/// <param name="pushApplicationId">The push application id.</param>
		/// <param name="listeners">The listeners.</param>
		public void AddListeners(string pushApplicationId, IEnumerable<NotificationListener> listeners)
		{
			_listeners.AddOrUpdate(pushApplicationId, listeners.ToList(),
				(pushApp, existingList) =>
				{
					existingList.AddRange(listeners);
					return existingList;
				});
		}

		/// <summary>
		/// Passes the notification to qualifying listener objects.
		/// </summary>
		/// <param name="notification">The notification.</param>
		public void NotifyListeners(Notification notification)
		{
			_notificationQueue.Post(notification);
		}

		private void ExecuteNotification(Notification notification)
		{
			List<NotificationListener> listeners = null;
			if (_listeners.TryGetValue(notification.PushApplicationId, out listeners))
			{
				// Determine whether the application is reliable
				bool isApplicationReliable = false;
				if (notification.QualityOfService != null &&
					notification.QualityOfService.DeliveryMethod == DeliveryMethod.Confirmed)
				{
					isApplicationReliable = true;
				}

				// Create push outcome and prepare to notify listeners
				PushOutcome outcome = new PushOutcome(
					notification.Address,
					notification.Code,
					notification.Description,
					notification.EventTime,
					notification.MessageState,
					isApplicationReliable);

				// Walk the list of listeners
				foreach (var listener in listeners)
				{
					try
					{
						if ((listener.ListenerType & ListenerType.Success) != 0 && notification.MessageState == MessageState.Delivered)
						{
							listener.Process(notification.PushId, ListenerType.Success, outcome);
						}
						else if ((listener.ListenerType & ListenerType.Failure) != 0 && notification.MessageState != MessageState.Delivered)
						{
							listener.Process(notification.PushId, ListenerType.Failure, outcome);
						}
						else if ((listener.ListenerType & ListenerType.Last) != 0 && notification.IsLast)
						{
							listener.Process(notification.PushId, ListenerType.Last, outcome);
						}
					}
					catch(PushSDKException)
					{
						// TODO: Trace some stuff
					}
				}
			}
		}
	}
}
